package com.geniusk.chat;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
//import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONUtil;
import com.geniusk.chat.core.ChatContent;
import com.geniusk.chat.core.ChatMessage;
import com.geniusk.chat.enums.ChatActionEnum;
import com.geniusk.common.enums.ResponseType;
import com.geniusk.common.exception.GraceException;
import com.geniusk.common.utils.JsonUtil;
import com.geniusk.common.utils.PageUtil;
import com.geniusk.common.utils.RedisUtil;
import com.geniusk.common.utils.SpringContextUtil;
import com.geniusk.model.pojo.ChatMsg;
import com.geniusk.model.vo.MessageVO;
import com.geniusk.security.core.LoginUser;
import com.geniusk.security.utils.TokenUtil;
import com.geniusk.service.ChatMsgService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 处理消息的handler
 * TextWebSocketFrame： 在netty中，是用于为websocket专门处理文本的对象，frame是消息的载体
 */
@Slf4j
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

	private final ChatMsgService chatMsgService = (ChatMsgService) SpringContextUtil.getBean("chatMsgServiceImpl");

	// 用于记录和管理所有客户端的channel
	public static final ChannelGroup wssChannelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

	private static final TokenUtil tokenUtil = SpringContextUtil.getBean(TokenUtil.class);

	private final RedisUtil redisUtil = SpringContextUtil.getBean(RedisUtil.class);

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
		// 获取客户端传输过来的消息
		String content = msg.text();
		Channel currentChannel = ctx.channel();
		// 获取客户端发来的消息
		ChatContent currentChatContent = JSONUtil.parse(content).toBean(ChatContent.class);
		Integer action = currentChatContent.getAction();
		String token = currentChatContent.getToken();
		Long loginUserId = getLoginUserIdByToken(token);
		// 判断消息类型，根据不同的类型来处理不同的业务
		if (action.equals(ChatActionEnum.CONNECT.type)) {
			// 当websocket 第一次open的时候，初始化channel，把用的channel和userID关联起来
			ChatSession.setChannel(loginUserId, currentChannel);
			ChatSession.output();
		}else if(action.equals(ChatActionEnum.UNREAD.type)){
			sendUnreadCount(loginUserId, currentChannel);
		} else if (action.equals(ChatActionEnum.MESSAGE.type)) {
			ChatContent chatContent = new ChatContent();
			chatContent.setAction(ChatActionEnum.MESSAGE.type);
			List<MessageVO> messageVOList = chatMsgService.selectMsgListByUserId(loginUserId);
			chatContent.setMessage(JSONUtil.toJsonStr(messageVOList));
			currentChannel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(chatContent)));
		}else if(action.equals(ChatActionEnum.CHAT.type)){
			Long receiverId = currentChatContent.getReceiverId();
			JSONObject jsonObject = JSONUtil.parseObj(currentChatContent.getMessage());
			int pageNum = Integer.parseInt(jsonObject.get("pageNum").toString());
			int pageSize = Integer.parseInt(jsonObject.get("pageSize").toString());
			PageUtil.startPage(pageNum,pageSize);
			ChatMsg selectChatMsg = new ChatMsg();
			selectChatMsg.setSenderId(loginUserId);
			selectChatMsg.setReceiverId(receiverId);
			List<ChatMsg> chatMessageList = chatMsgService.selectChatMsgByPage(selectChatMsg);
			Collections.reverse(chatMessageList);
			Map<String,Object> result = PageUtil.getResultTableData(chatMessageList);
			ChatContent chatContent = new ChatContent();
			chatContent.setAction(ChatActionEnum.CHAT.type);
			chatContent.setMessage(result);
			currentChannel.writeAndFlush(new TextWebSocketFrame(JsonUtil.objectToJson(chatContent)));
		}else if (action.equals(ChatActionEnum.SEND.type)) {
			// 解析数据
			Long receiverId = currentChatContent.getReceiverId();
			ChatMessage chatMessage = JSONUtil.parse(currentChatContent.getMessage()).toBean(ChatMessage.class);
			chatMessage.setMsgTimeFlag(false);
			chatMessage.setMsgSource("2");
			chatMessage.setSignFlag("0");
			chatMessage.setSenderId(loginUserId.toString());
			chatMessage.setReceiverId(receiverId.toString());
			ChatMsg chatMsg = new ChatMsg();
			Long msgId = IdUtil.getSnowflake().nextId();
			chatMessage.setMsgId(msgId.toString());
			chatMsg.setMsgId(msgId);
			chatMsg.setSenderId(loginUserId);
			chatMsg.setReceiverId(currentChatContent.getReceiverId());
			chatMsg.setMsg(chatMessage.getMsg());
			chatMsg.setMsgType(chatMessage.getMsgType());
			chatMsg.setMsgSource(chatMessage.getMsgSource());
			chatMsgService.insertChatMsg(chatMsg);
			ChatContent chatContent = new ChatContent();
			chatContent.setMessage(chatMessage);
			chatContent.setAction(ChatActionEnum.RECEIVE.type);
			//发送数据
			sendToWs(receiverId, chatContent);
			sendUnreadCount(receiverId, currentChannel);
		} else if (action.equals(ChatActionEnum.SIGNED.type)) {
			JSONArray jsonArray = (JSONArray) currentChatContent.getMessage();
			Long[] ids = jsonArray.toArray(new Long[0]);
			if(ids.length > 0){
				chatMsgService.signChatMsgByIds(ids);
			}

		} else if (action.equals(ChatActionEnum.DELETE.type)) {
			JSONArray jsonArray = (JSONArray) currentChatContent.getMessage();
			Long[] ids = jsonArray.toArray(new Long[0]);
			ArrayList<Long> receiverIds = new ArrayList<>();
			ArrayList<Long> sendIds = new ArrayList<>();
			for(Long msgId : ids){
				ChatMsg chatMsg = chatMsgService.selectChatMsgById(msgId);
				if(chatMsg.getReceiverId().equals(loginUserId)){
					receiverIds.add(msgId);
				}else if(chatMsg.getSenderId().equals(loginUserId)){
					sendIds.add(msgId);
				}
			}
			if(!receiverIds.isEmpty()){
				Long[] array = new Long[receiverIds.size()];
				array = receiverIds.toArray(array);
				chatMsgService.receiveDeleteByIds(array);
			}
			if(!sendIds.isEmpty()){
				Long[] array = new Long[sendIds.size()];
				array = sendIds.toArray(array);
				chatMsgService.sendDeleteByIds(array);
			}

		} else if (action.equals(ChatActionEnum.KEEPALIVE.type)) {
			currentChannel.id().asLongText();
		} else {
			log.info("无效连接 {}", currentChannel.id().asLongText());
		}

	}

	/**
	 * 发送未签收消息数量
	 * @param loginUserId 当前登陆用户ID
	 * @param currentChannel 当前信道
	 */
	private void sendUnreadCount(Long loginUserId, Channel currentChannel){
		ChatContent chatContent = new ChatContent();
		chatContent.setAction(ChatActionEnum.UNREAD.type);
		int count = chatMsgService.selectUnreadMsgCountByUserId(loginUserId);
		chatContent.setMessage(Integer.toString(count));
		currentChannel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(chatContent)));
	}

	/**
	 *
	 * @param userId 用户ID
	 * @param chatContent 消息
	 */
	private void sendToWs(Long userId, ChatContent chatContent){
		// 从全局用户Channel关系中获取接受方的channel
		List<Channel> receiverChannelList = ChatSession.getChannelListByUserId(userId);
		if (receiverChannelList != null) {
			// 当receiverChannel不为空的时候，从ChannelGroup去查找对应的channel是否存在
			for(Channel receiverChannel: receiverChannelList){
				Channel findChannel = wssChannelGroup.find(receiverChannel.id());
				if (findChannel != null) {
					// 用户在线
					receiverChannel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(chatContent)));
					sendUnreadCount(userId, findChannel);
				} else {
					// 用户离线，推送消息
//					log.info("接收用户离线, 接受者身份ID:{}, 接收信道：{}",userId, receiverChannel.id());
				}
			}
		}
	}

	/**
	 * 当客户端连接服务端之后（打开连接）
	 * 获取客户端的channel，并且放到ChannelGroup中去进行管理
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) {
		String channelId = ctx.channel().id().asLongText();
//		log.info("客户端已连接，channelId为：{}", channelId);
		wssChannelGroup.add(ctx.channel());
	}

	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) {
		String channelId = ctx.channel().id().asLongText();
//		log.info("客户端被移除，channelId为：{}", channelId);
		// 当触发handlerRemoved，ChannelGroup会自动移除对应客户端的channel
		removeUserId(channelId);
	}

	/**
	 * 捕获异常
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		log.error(cause.getMessage());
		Channel receiverChannel = ctx.channel();
		Channel findChannel = wssChannelGroup.find(receiverChannel.id());
		if (findChannel != null) {
			// 用户在线
			receiverChannel.writeAndFlush(new TextWebSocketFrame(cause.getMessage()));
		}
		// 发生异常之后关闭channel
		ctx.channel().close();
		// 随后从ChannelGroup中移除对应的channel
		wssChannelGroup.remove(ctx.channel());
		String channelId = ctx.channel().id().asLongText();
		// 移除多余会话
		removeUserId(channelId);
	}

	public Long getLoginUserIdByToken(String token){
		if(StrUtil.isBlank(token)){
			GraceException.display(ResponseType.FORBIDDEN);
		}
		LoginUser loginUser = tokenUtil.getLoginUserByToken(token);
		return loginUser.getUserId();
	}

	/**
	 * 移除多余会话
	 * @param channelId 信道ID
	 */
	public void removeUserId(String channelId){
		Long userId = ChatSession.getUserIdByChannelId(channelId);
		if(userId != null){
			ChatSession.removeChannel(userId,channelId);
		}
	}
}
